// Dataset
foo.DataSet = Class.create({
	initialize: function(){
		this.data = {};
		this.clients = [];
		this.servers = [];
		this.queue = [];
		this.ready = false;
	},
	
	get: function(){
		if(this.ready)
			return (this.data instanceof Array) ? this.data.clone() : Object.clone(this.data);
		else
			return false;
	},
	
	addDependency: function(ds) {
		if(typeof ds == 'string')
			ds = window[ds];
		
		if(ds.register)
			ds.register(this);
	},
	
	removeDependencies: function(ds) {
		if(typeof ds == 'string')
			ds = window[ds];
		
		if(ds.unregister)
			ds.unregister(this);
	},
	
	// Queue
	addQueue: function(queue) {
		if(typeof queue != 'function')
			return false;
		this.queue.push(queue);
		return true;
	},
	
	removeQueue: function(queue) {
		this.queue = this.queue.without(queue);
	},
	
	register: function(client){
		if(!this.clients.include(client)){
			this.clients.push(client);
			client.servers.push(this);
		}
	},
	
	unregister: function(client){
		this.clients = this.clients.without(client);
		client.servers = client.servers.without(this);
	},
	
	set: function(obj){
		Object.extend(this, obj);
		this.refresh();
	},
	
	update: function(){
		this.queue.invoke('apply', this);
		this.clients.invoke('refresh');
	},
	
	refresh: function(){
		this.update();
	}
});

foo.XHRDataSet = Class.create(foo.DataSet, {
	initialize: function($super, url, set){
		$super();
		
		this.url = url;
		this.path = '';
		this.period = false;
		this.method = 'get',
		this.parameters = '';
		
		if(set){
			var options = set.options;
			delete set.options;
			Object.extend(this, set);
		}
		
		this.options = {
			method: this.method,
			onSuccess: this.successHandler.bind(this),
			onFailure: this.failHandler.bind(this),
			onComplete: this.completeHandler.bind(this)
		};
		
		Object.extend(this.options, options || {});
		
		if(this.url)
			this.refresh();
	},
	
	getURL: function(){
		var url;
		try{
			url = eval(foo.parseInline(this.url));
		}catch(e){
			url = false;
		}
		return url;
	},
	
	getPath: function(){
		return eval(foo.parseInline(this.path));
	},
	
	successHandler: function(res){
		this.ready = true;
		var data = res.responseText;
		
		this.data = data;
		
		this.update();
	},
	
	failHandler: function(res) {
		this.ready = false;
		alert('Cannot load data');
	},
	
	completeHandler: function(res) {
		this.clients.invoke('loaded');
	},
	
	request: function(opt){
		Object.extend(this.options, opt);
		this.refresh();
	},
	
	update: function($super){
		$super();
		if(this.period != false){
			setTimeout(this.refresh.bind(this), this.period*1000);
		}
	},
	
	refresh: function(){
		var url = this.getURL();
		if(this.nocache)
			Object.extend(this.parameters, {_nocache: new Date().getTime()});
		if(url){
			new Ajax.Request(url, Object.extend({method: this.method,parameters: this.paremeters}, this.options));
			this.clients.invoke('update');
		}
	}
});

foo.JSONDataSet = Class.create(foo.XHRDataSet, {
	successHandler: function(res){
		this.ready = true;
		var data = res.responseText.evalJSON();
		
		this.data = data;
		if(this.path){
			try{
				this.data = eval('data.'+this.getPath());
			}catch(e){
				this.data = false;
			}
		}
		
		this.update();
	}
});

foo.XMLDataSet = Class.create(foo.XHRDataSet, {
	successHandler: function(res){
		this.ready = true;
		var data = foo.Utils.convertXMLToJSON(res.responseXML);
		
		this.data = data;
		if(this.path){
			try{
				this.data = eval('data.'+this.getPath());
			}catch(e){
				this.data = false;
			}
		}
		
		this.update();
	}
});

foo.TableDataSet = Class.create(foo.DataSet, {
	initialize: function($super){
		$super();
		this.data = [];
		this.ready = true;
		this.refresh();
	},
	
	add: function(data){
		this.data.push(data);
		this.refresh();
	},
	
	remove: function(data){
		var mark = false, res = [];
		for(var i=this.data.length-1; i>=0; i--){
			if(!mark && this.data[i] == data){
				mark = true;
				continue;
			}
			res.push(this.data[i]);
		}
		
		this.data = res.reverse();
		this.refresh();
	},
	
	removeAll: function(data){
		this.data = this.data.without(data);
	},
	
	include: function(data){
		this.data = this.data.concat(data);
		this.refresh();
	},
	
	clear: function(){
		this.data.clear();
		this.refresh();
	},
	
	get: function(fn){
		if(fn)
			return this.data.select(fn);
		else
			return this.data;
	}
});